 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.editors.text;

 import java.util.ArrayList ;
 import java.util.Collection ;
 import java.util.HashMap ;
 import java.util.HashSet ;
 import java.util.Iterator ;
 import java.util.List ;
 import java.util.Map ;
 import java.util.Set ;

 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.BusyIndicator;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Tree;

 import org.eclipse.core.runtime.ListenerList;
 import org.eclipse.core.runtime.SafeRunner;

 import org.eclipse.jface.util.SafeRunnable;
 import org.eclipse.jface.viewers.CheckStateChangedEvent;
 import org.eclipse.jface.viewers.CheckboxTableViewer;
 import org.eclipse.jface.viewers.CheckboxTreeViewer;
 import org.eclipse.jface.viewers.ICheckStateListener;
 import org.eclipse.jface.viewers.ILabelProvider;
 import org.eclipse.jface.viewers.ISelectionChangedListener;
 import org.eclipse.jface.viewers.IStructuredContentProvider;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.ITreeContentProvider;
 import org.eclipse.jface.viewers.ITreeViewerListener;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.viewers.TreeExpansionEvent;
 import org.eclipse.jface.viewers.ViewerComparator;

 /*
  * XXX: This is an copy of the internal ResourceTreeAndListGroup class, see:
  * https://bugs.eclipse.org/bugs/show_bug.cgi?id=147027
  */

 /**
  * Workbench-level composite that combines a CheckboxTreeViewer and
  * CheckboxListViewer. All viewer selection-driven interactions are handled
  * within this object
  */
 class SelectResourcesBlock implements ICheckStateListener, ISelectionChangedListener, ITreeViewerListener {


     /**
      * The IElementFilter is a interface that defines
      * the API for filtering the current selection of
      * a ResourceTreeAndListGroup in order to find a
      * subset to update as the result of a type filtering.
      * This is meant as an internal class and is used exclusively
      * by the import dialog.
      */
     interface IElementFilter {

         void filterElements(Collection elements) throws InterruptedException ;

         void filterElements(Object [] elements) throws InterruptedException ;
     }


     private Object root;

     private Object currentTreeSelection;

     private Collection expandedTreeNodes= new HashSet ();

     private Map checkedStateStore= new HashMap (9);

     private Collection whiteCheckedTreeItems= new HashSet ();

     private ListenerList listeners= new ListenerList(ListenerList.IDENTITY);

     private ITreeContentProvider treeContentProvider;

     private IStructuredContentProvider listContentProvider;

     private ILabelProvider treeLabelProvider;

     private ILabelProvider listLabelProvider;

     // widgets
 private CheckboxTreeViewer treeViewer;

     private CheckboxTableViewer listViewer;

     //height hint for viewers
 private static int PREFERRED_HEIGHT= 150;

     /**
      * Create an instance of this class. Use this constructor if you wish to
      * specify the width and/or height of the combined widget (to only hard code
      * one of the sizing dimensions, specify the other dimension's value as -1)
      *
      * @param parent
      * @param rootObject
      * @param treeContentProvider
      * @param treeLabelProvider
      * @param listContentProvider
      * @param listLabelProvider
      * @param style
      * @param useHeightHint If true then use the height hint to make this group big enough
      */
     public SelectResourcesBlock(Composite parent, Object rootObject, ITreeContentProvider treeContentProvider, ILabelProvider treeLabelProvider, IStructuredContentProvider listContentProvider, ILabelProvider listLabelProvider, int style, boolean useHeightHint) {

         root= rootObject;
         this.treeContentProvider= treeContentProvider;
         this.listContentProvider= listContentProvider;
         this.treeLabelProvider= treeLabelProvider;
         this.listLabelProvider= listLabelProvider;
         createContents(parent, style, useHeightHint);
     }

     /**
      * Add the passed listener to self's collection of clients that listen for
      * changes to element checked states
      *
      * @param listener ICheckStateListener
      */
     public void addCheckStateListener(ICheckStateListener listener) {
         listeners.add(listener);
     }

     /**
      * Iterates over the passed elements which are being realized for the
      * first time and check each one in the tree viewer as appropriate.
      *
      * @param elements the elements
      */
     private void checkNewTreeElements(Object [] elements) {
         for (int i= 0; i < elements.length; ++i) {
             Object currentElement= elements[i];
             boolean checked= checkedStateStore.containsKey(currentElement);
             treeViewer.setChecked(currentElement, checked);
             treeViewer.setGrayed(currentElement, checked && !whiteCheckedTreeItems.contains(currentElement));
         }
     }

     /**
      * An item was checked in one of self's two views. Determine which view this
      * occurred in and delegate appropriately
      *
      * @param event the check state event
      * @see org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged(org.eclipse.jface.viewers.CheckStateChangedEvent)
      */
     public void checkStateChanged(final CheckStateChangedEvent event) {

         //Potentially long operation - show a busy cursor
 BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), new Runnable () {
             public void run() {
                 if (event.getCheckable().equals(treeViewer))
                     treeItemChecked(event.getElement(), event.getChecked());
                 else
                     listItemChecked(event.getElement(), event.getChecked(), true);

                 notifyCheckStateChangeListeners(event);
             }
         });
     }

     /**
      * Lay out and initialize self's visual components.
      *
      * @param parent org.eclipse.swt.widgets.Composite
      * @param style the style flags for the new Composite
      * @param useHeightHint If true use the preferredHeight.
      */
     private void createContents(Composite parent, int style, boolean useHeightHint) {
         // group pane
 Composite composite= new Composite(parent, style);
         composite.setFont(parent.getFont());
         GridLayout layout= new GridLayout();
         layout.numColumns= 2;
         layout.makeColumnsEqualWidth= true;
         layout.marginHeight= 0;
         layout.marginWidth= 0;
         composite.setLayout(layout);
         composite.setLayoutData(new GridData(GridData.FILL_BOTH));

         createTreeViewer(composite, useHeightHint);
         createListViewer(composite, useHeightHint);

         initialize();
     }

     /**
      * Creates this block's list viewer.
      *
      * @param parent the parent control
      * @param useHeightHint if <code>true</code> use the height hints
      */
     private void createListViewer(Composite parent, boolean useHeightHint) {
         listViewer= CheckboxTableViewer.newCheckList(parent, SWT.BORDER);
         GridData data= new GridData(GridData.FILL_BOTH);
         if (useHeightHint)
             data.heightHint= PREFERRED_HEIGHT;
         listViewer.getTable().setLayoutData(data);
         listViewer.getTable().setFont(parent.getFont());
         listViewer.setContentProvider(listContentProvider);
         listViewer.setLabelProvider(listLabelProvider);
         listViewer.addCheckStateListener(this);
     }

     /**
      * Create this block's tree viewer.
      *
      * @param parent the parent control
      * @param useHeightHint if <code>true</code> use the height hints
      */
     private void createTreeViewer(Composite parent, boolean useHeightHint) {
         Tree tree= new Tree(parent, SWT.CHECK | SWT.BORDER);
         GridData data= new GridData(GridData.FILL_BOTH);
         if (useHeightHint)
             data.heightHint= PREFERRED_HEIGHT;
         tree.setLayoutData(data);
         tree.setFont(parent.getFont());

         treeViewer= new CheckboxTreeViewer(tree);
         treeViewer.setUseHashlookup(true);
         treeViewer.setContentProvider(treeContentProvider);
         treeViewer.setLabelProvider(treeLabelProvider);
         treeViewer.addTreeListener(this);
         treeViewer.addCheckStateListener(this);
         treeViewer.addSelectionChangedListener(this);
     }

     /**
      * Returns a boolean indicating whether the passed tree element should be at
      * LEAST gray-checked. Note that this method does not consider whether it
      * should be white-checked, so a specified tree item which should be
      * white-checked will result in a <code>true</code> answer from this
      * method.
      *
      * @param treeElement java.lang.Object
      * @return boolean
      */
     private boolean determineShouldBeAtLeastGrayChecked(Object treeElement) {
         // if any list items associated with treeElement are checked then it
 // retains its gray-checked status regardless of its children
 List checked= (List ) checkedStateStore.get(treeElement);
         if (checked != null && (!checked.isEmpty()))
             return true;

         // if any children of treeElement are still gray-checked then
 // treeElement
 // must remain gray-checked as well. Only ask expanded nodes
 if (expandedTreeNodes.contains(treeElement)) {
             Object [] children= treeContentProvider.getChildren(treeElement);
             for (int i= 0; i < children.length; ++i) {
                 if (checkedStateStore.containsKey(children[i]))
                     return true;
             }
         }

         return false;
     }

     /**
      * Expands an element in a tree viewer.
      *
      * @param element the element to be expanded
      */
     private void expandTreeElement(final Object element) {
         BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), new Runnable () {
             public void run() {

                 // First see if the children need to be given their checked
 // state at all. If they've
 // already been realized then this won't be necessary
 if (expandedTreeNodes.contains(element))
                     checkNewTreeElements(treeContentProvider.getChildren(element));
                 else {

                     expandedTreeNodes.add(element);
                     if (whiteCheckedTreeItems.contains(element)) {
                         //If this is the first expansion and this is a white
 // checked node then check the children
 Object [] children= treeContentProvider.getChildren(element);
                         for (int i= 0; i < children.length; ++i) {
                             if (!whiteCheckedTreeItems.contains(children[i])) {
                                 Object child= children[i];
                                 setWhiteChecked(child, true);
                                 treeViewer.setChecked(child, true);
                                 checkedStateStore.put(child, new ArrayList ());
                             }
                         }

                         //Now be sure to select the list of items too
 setListForWhiteSelection(element);
                     }
                 }

             }
         });
     }

     /**
      * Adds all of the selected children of <code>treeElement</code> to result recursively. This
      * does not set any values in the checked state.
      *
      * @param treeElement the tree element being queried
      * @param parentLabel the parent label
      * @param addAll a boolean to indicate if the checked state store needs to be queried
      * @param filter the filter being used on the data
      * @throws InterruptedException in case of interruption
      */
     private void findAllSelectedListElements(Object treeElement, String parentLabel, boolean addAll, IElementFilter filter) throws InterruptedException {

         String fullLabel= null;
     
         if (addAll)
             filter.filterElements(listContentProvider.getElements(treeElement));
         else { //Add what we have stored
 if (checkedStateStore.containsKey(treeElement))
                 filter.filterElements((Collection ) checkedStateStore.get(treeElement));
         }

         Object [] treeChildren= treeContentProvider.getChildren(treeElement);
         for (int i= 0; i < treeChildren.length; i++) {
             Object child= treeChildren[i];
             if (addAll)
                 findAllSelectedListElements(child, fullLabel, true, filter);
             else { //Only continue for those with checked state
 if (checkedStateStore.containsKey(child))
                     findAllSelectedListElements(child, fullLabel, whiteCheckedTreeItems.contains(child), filter);
             }

         }
     }

     /**
      * Find all of the white checked children of the treeElement and add them to
      * the collection. If the element itself is white select add it. If not then
      * add any selected list elements and recurse down to the children.
      *
      * @param treeElement java.lang.Object
      * @param result java.util.Collection
      */
     private void findAllWhiteCheckedItems(Object treeElement, Collection result) {

         if (whiteCheckedTreeItems.contains(treeElement))
             result.add(treeElement);
         else {
             Collection listChildren= (Collection ) checkedStateStore.get(treeElement);
             //if it is not in the store then it and it's children are not
 // interesting
 if (listChildren == null)
                 return;
             result.addAll(listChildren);
             Object [] children= treeContentProvider.getChildren(treeElement);
             for (int i= 0; i < children.length; ++i) {
                 findAllWhiteCheckedItems(children[i], result);
             }
         }
     }

     /**
      * Returns a flat list of all of the leaf elements which are checked. Filter
      * then based on the supplied ElementFilter. If monitor is canceled then
      * return null
      *
      * @param filter - the filter for the data
      * @throws InterruptedException in case of interruption
      */
     private void getAllCheckedListItems(IElementFilter filter) throws InterruptedException {
         //Iterate through the children of the root as the root is not in the store
 Object [] children= treeContentProvider.getChildren(root);
         for (int i= 0; i < children.length; ++i) {
             findAllSelectedListElements(children[i], null, whiteCheckedTreeItems.contains(children[i]), filter);
         }
     }

     /**
      * Returns a flat list of all of the leaf elements which are checked.
      *
      * @return all of the leaf elements which are checked. This API does not
      * return null in order to keep backwards compatibility.
      */
     public List getAllCheckedListItems() {

         final ArrayList returnValue= new ArrayList ();

         IElementFilter passThroughFilter= new IElementFilter() {

             public void filterElements(Collection elements) throws InterruptedException {
                 returnValue.addAll(elements);
             }

             public void filterElements(Object [] elements) throws InterruptedException {
                 for (int i= 0; i < elements.length; i++) {
                     returnValue.add(elements[i]);
                 }
             }
         };

         try {
             getAllCheckedListItems(passThroughFilter);
         } catch (InterruptedException exception) {
             return new ArrayList ();
         }
         return returnValue;

     }

     /**
      * Returns a list of all of the items that are white checked. Any folders
      * that are white checked are added and then any files from white checked
      * folders are added.
      *
      * @return the list of all of the items that are white checked
      */
     public List getAllWhiteCheckedItems() {

         List result= new ArrayList ();

         //Iterate through the children of the root as the root is not in the
 // store
 Object [] children= treeContentProvider.getChildren(root);
         for (int i= 0; i < children.length; ++i) {
             findAllWhiteCheckedItems(children[i], result);
         }

         return result;
     }

     /**
      * Logically gray-check all ancestors of <code>treeElement</code> by ensuring that they
      * appear in the checked table.
      *
      * @param treeElement the tree element
      */
     private void grayCheckHierarchy(Object treeElement) {

         //expand the element first to make sure we have populated for it
 expandTreeElement(treeElement);

         // if this tree element is already gray then its ancestors all are as
 // well
 if (checkedStateStore.containsKey(treeElement))
             return; // no need to proceed upwards from here

         checkedStateStore.put(treeElement, new ArrayList ());
         Object parent= treeContentProvider.getParent(treeElement);
         if (parent != null)
             grayCheckHierarchy(parent);
     }

     /**
      * Set the checked state of self and all ancestors appropriately. Do not
      * white check anyone - this is only done down a hierarchy.
      *
      * @param treeElement the tree element
      */
     private void grayUpdateHierarchy(Object treeElement) {

         boolean shouldBeAtLeastGray= determineShouldBeAtLeastGrayChecked(treeElement);

         treeViewer.setGrayChecked(treeElement, shouldBeAtLeastGray);

         if (whiteCheckedTreeItems.contains(treeElement))
             whiteCheckedTreeItems.remove(treeElement);

         // proceed up the tree element hierarchy
 Object parent= treeContentProvider.getParent(treeElement);
         if (parent != null) {
             grayUpdateHierarchy(parent);
         }
     }

     public void selectAndReveal(Object treeElement) {
         treeViewer.reveal(treeElement);
         IStructuredSelection selection= new StructuredSelection(treeElement);
         treeViewer.setSelection(selection);
     }

     /**
      * Initialize this group's viewers after they have been laid out.
      */
     private void initialize() {
         treeViewer.setInput(root);
         this.expandedTreeNodes= new ArrayList ();
         this.expandedTreeNodes.add(root);

     }

     /**
      * Callback that's invoked when the checked status of an item in the list is
      * changed by the user. Do not try and update the hierarchy if we are
      * building the initial list.
      *
      * @param listElement the list element
      * @param state the checked state
      * @param updatingFromSelection <code>true</code> if we are inside an
      * update caused by selection
      */
     private void listItemChecked(Object listElement, boolean state, boolean updatingFromSelection) {
         List checkedListItems= (List ) checkedStateStore.get(currentTreeSelection);
         //If it has not been expanded do so as the selection of list items will
 // affect gray state
 if (!expandedTreeNodes.contains(currentTreeSelection))
             expandTreeElement(currentTreeSelection);

         if (state) {
             if (checkedListItems == null) {
                 // since the associated tree item has gone from 0 -> 1 checked
 // list items, tree checking may need to be updated
 grayCheckHierarchy(currentTreeSelection);
                 checkedListItems= (List ) checkedStateStore.get(currentTreeSelection);
             }
             checkedListItems.add(listElement);
         } else {
             checkedListItems.remove(listElement);
             if (checkedListItems.isEmpty()) {
                 // since the associated tree item has gone from 1 -> 0 checked
 // list items, tree checking may need to be updated
 ungrayCheckHierarchy(currentTreeSelection);
             }
         }

         //Update the list with the selections if there are any
 if (checkedListItems.size() > 0)
             checkedStateStore.put(currentTreeSelection, checkedListItems);
         if (updatingFromSelection)
             grayUpdateHierarchy(currentTreeSelection);
     }

     /**
      * Notify all checked state listeners with the given event.
      *
      * @param event the event
      */
     private void notifyCheckStateChangeListeners(final CheckStateChangedEvent event) {
         Object [] array= listeners.getListeners();
         for (int i= 0; i < array.length; i++) {
             final ICheckStateListener l= (ICheckStateListener) array[i];
             SafeRunner.run(new SafeRunnable() {
                 public void run() {
                     l.checkStateChanged(event);
                 }
             });
         }
     }

     /**
      * Set the contents of the list viewer based upon the specified selected
      * tree element. This also includes checking the appropriate list items.
      *
      * @param treeElement java.lang.Object
      */
     private void populateListViewer(final Object treeElement) {
         listViewer.setInput(treeElement);

         //If the element is white checked but not expanded we have not set up
 // all of the children yet
 if (!(expandedTreeNodes.contains(treeElement)) && whiteCheckedTreeItems.contains(treeElement)) {

             //Potentially long operation - show a busy cursor
 BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), new Runnable () {
                 public void run() {
                     setListForWhiteSelection(treeElement);
                     listViewer.setAllChecked(true);
                 }
             });

         } else {
             List listItemsToCheck= (List ) checkedStateStore.get(treeElement);

             if (listItemsToCheck != null) {
                 Iterator listItemsEnum= listItemsToCheck.iterator();
                 while (listItemsEnum.hasNext())
                     listViewer.setChecked(listItemsEnum.next(), true);
             }
         }
     }

     /**
      * Logically gray-check all ancestors of <code>item</code> by ensuring
      * that they appear in the checked table. Add any elements to the
      * <code>selectedNodes</code> so we can track that has been done.
      *
      * @param item the tree item
      * @param selectedNodes the set of selected nodes
      */
     private void primeHierarchyForSelection(Object item, Set selectedNodes) {

         //Only prime it if we haven't visited yet
 if (selectedNodes.contains(item))
             return;

         checkedStateStore.put(item, new ArrayList ());

         //mark as expanded as we are going to populate it after this
 expandedTreeNodes.add(item);
         selectedNodes.add(item);

         Object parent= treeContentProvider.getParent(item);
         if (parent != null)
             primeHierarchyForSelection(parent, selectedNodes);
     }

     /**
      * Remove the passed listener from self's collection of clients that listen
      * for changes to element checked states
      *
      * @param listener ICheckStateListener
      */
     public void removeCheckStateListener(ICheckStateListener listener) {
         listeners.remove(listener);
     }

     /**
      * Handle the selection of an item in the tree viewer
      *
      * @param event SelectionChangedEvent
      */
     public void selectionChanged(SelectionChangedEvent event) {
         IStructuredSelection selection= (IStructuredSelection) event.getSelection();
         Object selectedElement= selection.getFirstElement();
         if (selectedElement == null) {
             currentTreeSelection= null;
             listViewer.setInput(currentTreeSelection);
             return;
         }

         // i.e.- if not an item deselection
 if (selectedElement != currentTreeSelection)
             populateListViewer(selectedElement);

         currentTreeSelection= selectedElement;
     }

     /**
      * Select or deselect all of the elements in the tree depending on the value
      * of <code>selection</code>. Be sure to update the displayed files as
      * well.
      *
      * @param selection <code>true</code> if selection should be set,
      * <code>false</code> if it should be removed
      */
     public void setAllSelections(final boolean selection) {

         //If there is no root there is nothing to select
 if (root == null)
             return;

         //Potentially long operation - show a busy cursor
 BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), new Runnable () {
             public void run() {
                 setTreeChecked(root, selection);
                 listViewer.setAllChecked(selection);
             }
         });
     }

     /**
      * The treeElement has been white selected. Get the list for the element and
      * set it in the checked state store.
      *
      * @param treeElement the element being updated
      */
     private void setListForWhiteSelection(Object treeElement) {

         Object [] listItems= listContentProvider.getElements(treeElement);
         List listItemsChecked= new ArrayList ();
         for (int i= 0; i < listItems.length; ++i) {
             listItemsChecked.add(listItems[i]);
         }

         checkedStateStore.put(treeElement, listItemsChecked);
     }

     /**
      * Set the <code>comparator</code> that is to be applied to self's list viewer
      *
      * @param comparator the comparator to be set
      */
     public void setListComparator(ViewerComparator comparator) {
         listViewer.setComparator(comparator);
     }

     /**
      * Set the checked state of the passed <code>treeElement</code>
      * appropriately, and do so recursively to all of its child tree elements as
      * well.
      *
      * @param treeElement the root of the subtree
      * @param state <code>true</code> if checked, <code>false</code> otherwise
      */
     private void setTreeChecked(Object treeElement, boolean state) {

         if (treeElement.equals(currentTreeSelection)) {
             listViewer.setAllChecked(state);
         }

         if (state) {
             setListForWhiteSelection(treeElement);
         } else
             checkedStateStore.remove(treeElement);

         setWhiteChecked(treeElement, state);
         treeViewer.setChecked(treeElement, state);
         treeViewer.setGrayed(treeElement, false);

         // now logically check/uncheck all children as well if it has been
 // expanded
 if (expandedTreeNodes.contains(treeElement)) {
             Object [] children= treeContentProvider.getChildren(treeElement);
             for (int i= 0; i < children.length; ++i) {
                 setTreeChecked(children[i], state);
             }
         }
     }

     /**
      * Set the comparator that is to be applied to self's tree viewer.
      *
      * @param comparator the comparator to be set
      */
     public void setTreeComparator(ViewerComparator comparator) {
         treeViewer.setComparator(comparator);
     }

     /**
      * Adjust the collection of references to white-checked tree elements
      * appropriately.
      *
      * @param treeElement the root of the subtree
      * @param isWhiteChecked <code>true</code> if white checked,
      * <code>false</code> otherwise
      */
     private void setWhiteChecked(Object treeElement, boolean isWhiteChecked) {
         if (isWhiteChecked) {
             if (!whiteCheckedTreeItems.contains(treeElement))
                 whiteCheckedTreeItems.add(treeElement);
         } else
             whiteCheckedTreeItems.remove(treeElement);
     }

     /**
      * Handle the collapsing of an element in a tree viewer.
      *
      * @param event the collapse event
      */
     public void treeCollapsed(TreeExpansionEvent event) {
         // We don't need to do anything with this
 }

     /**
      * Handle the expansion of an element in a tree viewer.
      *
      * @param event the expansion event
      */
     public void treeExpanded(TreeExpansionEvent event) {
         expandTreeElement(event.getElement());
     }

     /**
      * Callback that's invoked when the checked status of an item in the tree is
      * changed by the user.
      *
      * @param treeElement the tree element that has been checked/unchecked
      * @param state <code>true</code> if checked, <code>false</code> if
      * unchecked
      */
     private void treeItemChecked(Object treeElement, boolean state) {

         // recursively adjust all child tree elements appropriately
 setTreeChecked(treeElement, state);

         Object parent= treeContentProvider.getParent(treeElement);
         if (parent == null)
             return;

         // now update upwards in the tree hierarchy
 if (state)
             grayCheckHierarchy(parent);
         else
             ungrayCheckHierarchy(parent);

         //Update the hierarchy but do not white select the parent
 grayUpdateHierarchy(parent);
     }

     /**
      * Logically un-gray-check all ancestors of <code>treeElement</code> if
      * appropriate.
      *
      * @param treeElement the root of the subtree
      */
     private void ungrayCheckHierarchy(Object treeElement) {
         if (!determineShouldBeAtLeastGrayChecked(treeElement))
             checkedStateStore.remove(treeElement);

         Object parent= treeContentProvider.getParent(treeElement);
         if (parent != null)
             ungrayCheckHierarchy(parent);
     }

     /**
      * Update the selections of the tree elements in items to reflect the new
      * selections provided.
      *
      * @param items Map with keys of Object (the tree element) and values of
      * List (the selected list elements). NOTE: This method does not
      * special case keys with no values (i.e., a tree element with an
      * empty list). If a tree element does not have any selected
      * items, do not include the element in the Map.
      */
     public void updateSelections(Map items) {
         // We are replacing all selected items with the given selected items,
 // so reinitialize everything.
 this.listViewer.setAllChecked(false);
         this.treeViewer.setCheckedElements(new Object [0]);
         this.whiteCheckedTreeItems= new HashSet ();
         Set selectedNodes= new HashSet ();
         checkedStateStore= new HashMap ();

         //Update the store before the hierarchy to prevent updating parents
 // before all of the children are done
 Iterator keyIterator= items.keySet().iterator();
         while (keyIterator.hasNext()) {
             Object key= keyIterator.next();
             primeHierarchyForSelection(key, selectedNodes);
             checkedStateStore.put(key, items.get(key));
         }

         // Update the checked tree items. Since each tree item has a selected
 // item, all the tree items will be gray checked.
 treeViewer.setCheckedElements(checkedStateStore.keySet().toArray());
         treeViewer.setGrayedElements(checkedStateStore.keySet().toArray());

         // Update the listView of the currently selected tree item.
 if (currentTreeSelection != null) {
             Object displayItems= items.get(currentTreeSelection);
             if (displayItems != null)
                 listViewer.setCheckedElements(((List ) displayItems).toArray());
         }
     }
 }

